﻿using System;
using System.Windows.Forms;
using System.Drawing;
using System.Linq;
using System.Collections.Generic;
using OpenGL;
using System.Text;
using System.IO;
using OSM;

namespace DiceWars
{
	class MainForm : GLForm
	{
		DiceWar D = new DiceWar();

		Graph_Admin MapBase = new Graph_Admin();

		V3D CoordToLocal(double lng, double lat)
		{
			return new V3G(19, 47).GetLocalDirection(new V3G(lng, lat)).RotX(-90) * 0.00001;
		}

		//List<double[]> edges = new List<double[]>();
		//List<double[]> edgecolors = new List<double[]>();

		public MainForm(int x, int y, int w, int h)
			: base()
		{
			MapBase.Load("C:\\MyApp\\dw2.graph");
			Console.WriteLine("Loaded " + MapBase.Points.Count + ", " + MapBase.Edges.Count);

			Location = new Point(x, y);
			Size = new Size(w, h);
			CreateTestMap(ref D);

			foreach (DiceBorder e in D.Territories.Edges)
			{
				List<double> edge = new List<double>();
				//int N = edges.Count / 3;
				//int N2 = N + e.Way.NodeObj.Count;
				//e.StartID = N;
				//e.EndID = N2;
				for (int i = 0; i < e.Way.NodeObj.Count; i++)
				{
					V3D P = CoordToLocal(e.Way.NodeObj[i].Longit, e.Way.NodeObj[i].Latit);
					edge.Add(P.X);
					edge.Add(P.Y);
					edge.Add(P.Z);
					//edgecol.Add(1);
					//edgecol.Add(1);
					//edgecol.Add(1);
				}
				e.edge = edge.ToArray();
				//edges.Add(edge.ToArray());
				//edgecolors.Add(edgecol.ToArray());
			}
			UpdateTable();
			CurrentPlayer = SelectedPlayer = D.Players[0];
		}
		void CreateTestMap(ref DiceWar d)
		{
			d.Players.Clear();
			d.CreatePlayer("CPU1", V3D.Red);
			d.CreatePlayer("CPU2", V3D.Blue);
			d.CreatePlayer("CPU3", V3D.Green);
			d.CreatePlayer("CPU4", V3D.Yellow);
			d.CreatePlayer("CPU5", V3D.Purple);
			d.CreatePlayer("CPU6", new V3D(1, 0.5, 1));
			d.CreatePlayer("CPU7", V3D.Orange);
			d.CreatePlayer("CPU8", V3D.Green * 0.5);

			Dictionary<GraphPoint<V3G>, Territory> t = new Dictionary<GraphPoint<V3G>, Territory>();
			foreach (var p in MapBase.Points)
			{
				Territory T = new Territory(CoordToLocal(p.Content.Longitude, p.Content.Latitude));
				p.Tag = T;
				T.OwnBorder = (p as Graph_Territory).OwnBorder;
				t.Add(p, T);
			}
			foreach (var e in MapBase.Edges)
			{
				DiceBorder B = new DiceBorder(e.P1.Tag as Territory, e.P2.Tag as Territory, (e as Edge_Border).Way);
				d.Territories.Bind(B);
			}

			foreach (var p in t)
			{
				d.Territories.Add(p.Value);
				d.TList.Add(p.Value);
				foreach (var a in p.Key.Adjacents)
				{
					d.Territories.Bind(p.Value, t[a.Key], 1);
					//p.Value.Bind(t[a.Key], a.Value);
				}
			}
			Random R = new Random();
			foreach (var x in d.TList)
			{
				x.Owner = d.Players[R.Next(0, 8)];
				x.NumDice = 1;
			}
			d.UpdateStat();
		}
		void CreateTestMap2(ref DiceWar d)
		{
			d.Players.Clear();
			d.Territories.Clear();
			d.CreatePlayer("CPU1", new V3D(179, 127, 254) / 255.0);//Purple
			d.CreatePlayer("CPU2", new V3D(179, 255, 254) / 255.0);//Cyan
			d.CreatePlayer("CPU3", new V3D(255, 88, 88) / 255.0);//Red
			d.CreatePlayer("CPU4", new V3D(255, 127, 254) / 255.0);//Pink
			d.CreatePlayer("CPU5", new V3D(0, 147, 2) / 255.0);//Green
			d.CreatePlayer("CPU6", new V3D(179, 255, 1) / 255.0);//Lime
			d.CreatePlayer("CPU7", new V3D(255, 127, 1) / 255.0);//Orange
			d.CreatePlayer("CPU8", new V3D(255, 255, 1) / 255.0);//Yellow

			d.CreateTerritory(new V3D(112, 0, 64), 6, 3);
			d.CreateTerritory(new V3D(55, 0, 133), 3, 2);
			d.CreateTerritory(new V3D(122, 0, 186), 5, 3);
			d.CreateTerritory(new V3D(205, 0, 172), 0, 2);
			d.CreateTerritory(new V3D(77, 0, 246), 4, 3);
			d.CreateTerritory(new V3D(123, 0, 271), 4, 3);
			d.CreateTerritory(new V3D(43, 0, 345), 6, 2);
			d.CreateTerritory(new V3D(146, 0, 328), 3, 3);

			d.CreateTerritory(new V3D(239, 0, 328), 1, 3);

			d.CreateTerritory(new V3D(308, 0, 273), 7, 4);
			d.CreateTerritory(new V3D(330, 0, 357), 2, 4);
			d.CreateTerritory(new V3D(272, 0, 390), 0, 3);
			d.CreateTerritory(new V3D(410, 0, 286), 1, 3);
			d.CreateTerritory(new V3D(434, 0, 344), 5, 2);

			d.CreateTerritory(new V3D(492, 0, 272), 7, 2);
			d.CreateTerritory(new V3D(515, 0, 354), 4, 3);
			d.CreateTerritory(new V3D(582, 0, 301), 6, 2);
			d.CreateTerritory(new V3D(490, 0, 217), 4, 2);
			d.CreateTerritory(new V3D(596, 0, 259), 2, 2);
			d.CreateTerritory(new V3D(664, 0, 316), 3, 3);
			d.CreateTerritory(new V3D(732, 0, 286), 6, 4);

			d.CreateTerritory(new V3D(537, 0, 132), 5, 4);
			d.CreateTerritory(new V3D(583, 0, 162), 5, 3);
			d.CreateTerritory(new V3D(650, 0, 187), 0, 3);
			d.CreateTerritory(new V3D(707, 0, 228), 0, 4);

			d.CreateTerritory(new V3D(620, 0, 63), 2, 4);
			d.CreateTerritory(new V3D(676, 0, 132), 3, 3);
			d.CreateTerritory(new V3D(732, 0, 90), 1, 4);
			d.CreateTerritory(new V3D(676, 0, 19), 7, 4);
			foreach (Territory T in d.TList)
				T.Content = new V3D(T.Content.X / 100 - 4, 0, T.Content.Z / 100 - 2);
			/*d.Bind(0, 1);
			d.Bind(1, 2, 4);
			d.Bind(2, 3, 5);
			d.Bind(3, 5);
			d.Bind(4, 5, 6);
			d.Bind(5, 6, 7, 8);
			d.Bind(6, 7);
			d.Bind(7, 8);
			d.Bind(8, 9, 10, 11);
			d.Bind(9, 10, 12);
			d.Bind(10, 11, 12, 13);
			d.Bind(11, 13);
			d.Bind(12, 14);
			d.Bind(13, 14, 15);
			d.Bind(14, 15, 16, 17, 18);
			d.Bind(15, 16, 19);
			d.Bind(16, 18, 19);
			d.Bind(17, 18, 21, 22);
			d.Bind(18, 19, 22, 23, 24);
			d.Bind(19, 20, 24);
			d.Bind(20, 24);
			d.Bind(21, 22, 25, 26);
			d.Bind(22, 23, 26);
			d.Bind(23, 24, 26);
			d.Bind(24, 26);
			d.Bind(25, 26, 27, 28);
			d.Bind(26, 27);
			d.Bind(27, 28);
			*/
			for (int i = 0; i < -d.TList.Count; i++)
			{
				d.TList[i].Owner = i < 15 ? d.Players[0] : d.Players[1];
				d.TList[i].NumDice = 8;
			}
			//D.PlayImmediate(D.Players[0]);
			//GC.Collect();
		}
		Player SelectedPlayer;
		Player CurrentPlayer;

		void UpdateTable()
		{
			TimerLog T = new TimerLog();
			//T.Start();
			//T.Log("Start");
			D.UpdateStat();
			//T.Log("Stat upd");
			for (int i = 0; i < D.Territories.Edges.Count; i++)
			{
				var E = D.Territories.Edges[i];
				//if ((E.P1 as Territory).Owner == (E.P2 as Territory).Owner && E.P1.Dij_Size == D.Territories.MaxAreas[E.P1.Dij_Group].Dij_Size)
				//	SetEdgeColor(i, D.Players[E.P1.Dij_Group].Color);
				//else
				//	SetEdgeColor(i, V3D.White * 0.5);
			}
			//T.Log("Set edges");
		}
		protected override void ProcessGLKey(Keys K, bool shift, bool control)
		{
			if (K == Keys.Space)
				for (int round = 0; round < 1; round++)
				{
					for (int C = 0; C < 8; C++)
					{
						{
							if (CurrentPlayer == SelectedPlayer)
								D.Resolve(CurrentPlayer);
							else
								D.PlayImmediate(CurrentPlayer);
							D.Round++;
							UpdateTable();
							CurrentPlayer = D.Players[(D.Players.IndexOf(CurrentPlayer) + 1) % D.Players.Count];

							foreach (var x in ND)
							{
								if (!D.Players.Contains(x.Key))
									x.Value.Values.Add(new PT1(1, 1, 0));
							}
							for (int i = 0; i < -D.Players.Count; )
								if (D.Players[i].NumTerr == 0)
									D.Players.RemoveAt(i);
								else
									i++;
							//SelectedPlayer = D.Players[(D.Players.IndexOf(SelectedPlayer) + 1) % D.Players.Count];
						}
					}
					double Total = 0;
					foreach (Player P in D.Players)
						Total += P.NumDice;
					foreach (Player P in D.Players)
					{
						PT1 V = new PT1(1, 1, P.NumDice / Total);
						ND[P].Values.Add(V);
					}
				}
			else if (K == Keys.A)
			{
				List<Territory> newterr = DiceWar.AI2(SelectedPlayer, D, 0, selectedterr);
				newterr.AddRange(selectedterr);
				selectedterr = newterr.ToArray();
				D.UpdateStat();
				UpdateTable();
				GC.Collect();
			}
			else if (K == Keys.D)
			{
				SelectTerr();
			}
			else if (K == Keys.S)
			{
				SaveGame();
			}
			else if (K == Keys.L)
			{
				LoadGame();
				D.UpdateStat();
				UpdateTable();
			}
			else if (K == Keys.X)
			{
				if (mouseterr != null)
					mouseterr.Owner = SelectedPlayer;
			}
			else if (K == Keys.PageUp)
				Camera.Y /= 2.0;
			else if (K == Keys.PageDown)
				Camera.Y *= 2.0;
			else if (K == Keys.Up)
				Camera.Z -= 0.3;
			else if (K == Keys.Down)
				Camera.Z += 0.3;
			else if (K == Keys.Left)
				Camera.X -= 0.3;
			else if (K == Keys.Right)
				Camera.X += 0.3;
		}
		double f = 0;
		GLText Txt;

		Dictionary<Player, GLSeries> ND = new Dictionary<Player, GLSeries>();

		protected override void InitGL()
		{
			Txt = new GLText("Arial");
			foreach (Player P in D.Players)
				ND.Add(P, new GLSeries(0));
		}
		public void SetEdgeColor(GraphEdge<V3D> X, V3D c)
		{
			//SetEdgeColor(D.Territories.Edges.IndexOf(X), c);
		}
		public void SetEdgeColor(int N, V3D c)
		{
			//for (int i = 0; i < edgecolors[N].Length / 3; i++)
			{
				//edgecolors[N][i * 3] = c.X;
				//edgecolors[N][i * 3 + 1] = c.Y;
				//edgecolors[N][i * 3 + 2] = c.Z;
			}
		}
		protected override void Process(double dt, double tt)
		{
			//foreach (var X in ND)
			//	X.Value.Process(dt);
		}

		Territory[] selectedterr = new Territory[0];

		Territory mouseterr = null;


		void DrawTerritory(Territory T)
		{
			if (T.Owner == null)
				GL.glCol3D(V3D.White);
			else
				GL.glCol3D(T.Owner.Color);

			if (T.OwnBorder.Count > 0)
				GL.glCol3D(V3D.White);
			else
				GL.glCol3D(V3D.White * 0.3);
			double sel = T == mouseterr ? 5 : 2;
			GL.glBegin(GL.GL_TRIANGLE_FAN);
			GL.glV3D(T.Content);
			for (int i = 0; i <= 6; i++)
				GL.glV3D(T.Content + V3D.Red.RotY(360 / 4.0 * i) * 0.003 * sel);
			GL.glEnd();
		}
		V3D Camera = new V3D(0, 1.5, 1);

		protected override bool DrawGLScene()
		{
			MakePerspective(30);
			if (selecting)
				GL.glClearColor(0, 0, 0.5f, 0);
			else
				GL.glClearColor(0, 0, 0.0f, 0);
			GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
			GL.glLoadIdentity();
			GL.gluLookAt(Camera.X, Camera.Y, Camera.Z, Camera.X, 0, Camera.Z - 1, 0, 1, 0);
			CalculateMouse();
			Mouse3D = new Line3D(Camera, Mouse3D) * new Plane3D(V3D.Black, V3D.Green);
			{
				mouseterr = null;
				double d = 0.02;
				double d2;
				for (int i = 0; i < D.TList.Count; i++)
				{
					if ((d2 = (Mouse3D - D.TList[i].Content).Length) < d)
					{
						d = d2;
						mouseterr = D.TList[i];
					}
				}
			}
			for (int i = 0; i < D.Territories.Edges.Count; i++)
			{
				var e = D.Territories.Edges[i] as DiceBorder;
				if ((e.P1 as Territory).Owner != (e.P2 as Territory).Owner)
				{
					GL.glLineWidth(3);
					GL.glCol3D(V3D.White * 0.33);
				}
				else
					GL.glCol3D((e.P1 as Territory).Owner.Color);
				GL.glEnableClientState(GL.GL_VERTEX_ARRAY);
				GL.glVertexPointer3d(0, e.edge);
				GL.glDrawArrays(GL.GL_LINE_STRIP, 0, e.edge.Length / 3);
				GL.glDisableClientState(GL.GL_VERTEX_ARRAY);
				GL.glLineWidth(1);
			}
			foreach (var T in D.TList)
			{
				DrawTerritory(T);

				GL.glCol3D(T.Owner.Color);
				GL.BlendAlpha();
				if (T.NumDice > 1)
					Txt.DrawString(T.NumDice.ToString("0"), T.Content + new V3D(0, 0.001, 0), V3D.Red * 0.1, V3D.Red, -V3D.Blue * 0.03, 0.03, 0.03
						, StringAlignment.Center
						, StringAlignment.Center);
				GL.BlendOff();
			}
			if (selectedterr != null && selectedterr.Length > 0)
				foreach (var T in selectedterr)
				{
					GL.glCol3D(V3D.White);
					GL.glBegin(GL.GL_LINE_LOOP);
					for (int i = 0; i < 16; i++)
						GL.glV3D(T.Content + V3D.Red.RotY(360 / 16.0 * i) * 0.03);
					GL.glEnd();
				}

			MakeOrtho();
			GL.glDisable(GL.GL_DEPTH_TEST);
			GL.glPushMatrix();
			GL.glColor3d(0, 0, 0);
			GL.glTranslated(-Width / 2, -Height / 2, 0);
			GL.glBegin(GL.GL_QUADS);
			GL.glVertex3d(0, 0, 0);
			GL.glVertex3d(200, 0, 0);
			GL.glVertex3d(200, Height, 0);
			GL.glVertex3d(0, Height, 0);
			GL.glEnd();
			GL.glPopMatrix();
			GL.glEnable(GL.GL_DEPTH_TEST);

			for (int i = 0; i < D.Players.Count; i++)
			{
				GL.glCol3D(D.Players[i].Color);
				Txt.DrawString("Player " + i + (D.Players[i] == CurrentPlayer ? "*" : "") + ": " + D.Players[i].NumDice.ToString("000") + "(" + D.Players[i].Reserved.ToString("00") + " - " + D.Players[i].MaxTerr.ToString("00") + ")", -650, 350 - i * 15, 15, 15, StringAlignment.Near);
			}
			Txt.DrawString(mouseterr == null ? "-" : "1", -650, 0, 40, 40, StringAlignment.Center);
			Txt.DrawString(selectedterr == null ? "-" : selectedterr.Length.ToString(), -650, -40, 40, 40, StringAlignment.Center);
			foreach (var X in ND)
			{
				X.Value.DrawLines(-Width / 2, -Height / 3, 20, 500, X.Key.Color, 10);
			}
			return true;
		}
		V3D SelectPoint1;
		V3D SelectPoint2;
		bool selecting = false;
		protected override void OnMouseDown(MouseEventArgs e)
		{
			if (mouseterr == null)
			{
				SelectPoint1 = Mouse3D;
				selecting = true;
				return;
			}
			if (mouseterr.Owner == SelectedPlayer)
				selectedterr = new Territory[] { mouseterr };
			else if (selectedterr != null && selectedterr.Length == 1 && selectedterr[0].Adjacents.ContainsKey(mouseterr))
			{
				if (selectedterr[0].Attack(mouseterr, D.R))
					selectedterr[0] = mouseterr;
				else
					selectedterr = null;
				UpdateTable();
			}
		}

		void SelectTerr()
		{
			if (SelectPoint1.X > SelectPoint2.X)
				Algebra.Swap(ref SelectPoint1.X, ref SelectPoint2.X);
			if (SelectPoint1.Z > SelectPoint2.Z)
				Algebra.Swap(ref SelectPoint1.Z, ref SelectPoint2.Z);
			List<Territory> res = new List<Territory>();
			foreach (Territory T in D.Territories.Points)
				if (T.Owner == SelectedPlayer && T.Content.In2D(SelectPoint1, SelectPoint2) && T.NumDice > 1)
				{
					res.Add(T);
				}
			selectedterr = res.ToArray();
		}
		protected override void OnMouseUp(MouseEventArgs e)
		{
			if (selecting)
			{
				SelectPoint2 = Mouse3D;
				selecting = false;
				SelectTerr();
			}
			//else
			//	selectedterr = new Territory[] { mouseterr };
		}

		void LoadGame()
		{
			TextReader R = File.OpenText("savegame.dat");
			CurrentPlayer = D.Players[R.ReadInt32()];
			int N = R.ReadInt32();
			for (int i = 0; i < N; i++)
				D.Players[i].Reserved = R.ReadInt32();
			N = R.ReadInt32();
			for (int i = 0; i < N; i++)
				(D.Territories.Points[i] as Territory).SetOwnerAndDice(D.Players[R.ReadInt32()], R.ReadInt32());
			R.Close();
		}
		void SaveGame()
		{
			TextWriter W = File.CreateText("savegame.dat");
			W.WriteLine(D.Players.IndexOf(CurrentPlayer));
			W.WriteLine(D.Players.Count);
			for (int i = 0; i < D.Players.Count; i++)
				W.WriteLine(D.Players[i].Reserved);
			W.WriteLine(D.Territories.Points.Count);
			for (int i = 0; i < D.Territories.Points.Count; i++)
			{
				W.WriteLine(D.Players.IndexOf((D.Territories.Points[i] as Territory).Owner));
				W.WriteLine((D.Territories.Points[i] as Territory).NumDice);
			}
			W.Close();
		}
	}
	static partial class ext
	{
		public static int ReadInt32(this TextReader R)
		{
			return int.Parse(R.ReadLine());
		}
	}
}